以 Vue-cli3 初始化的進入點來說,傳入的 APP 是一個 Component
// min.js
import Vue from "vue";
import App from "./App.vue";
new Vue({
// h 是 createElement 方法
render: h => h(App)
}).$mount("#app");
調用 createComponent
方法來創建 VNode
,先看5個參數的定義
Ctor
: 可以是 Component Class 或 function 等data
: 元件的數據context
: 當前的 Vue 實例children
: 該元件的子節點(VNode)tag
: 不一定要帶此參數,若有要是 string 型別// src/core/vdom/create-component.js
export function createComponent (
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
if (isUndef(Ctor)) {
return
}
const baseCtor = context.$options._base
if (isObject(Ctor)) {
Ctor = baseCtor.extend(Ctor)
}
if (typeof Ctor !== 'function') {
if (process.env.NODE_ENV !== 'production') {
warn(`Invalid Component definition: ${String(Ctor)}`, context)
}
return
}
/****************************
判斷元件是否為異步元件
****************************/
let asyncFactory
if (isUndef(Ctor.cid)) {
asyncFactory = Ctor
Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
if (Ctor === undefined) {
return createAsyncPlaceholder(
asyncFactory,
data,
context,
children,
tag
)
}
}
data = data || {}
resolveConstructorOptions(Ctor)
if (isDef(data.model)) {
transformModel(Ctor.options, data)
}
// extract props
const propsData = extractPropsFromVNodeData(data, Ctor, tag)
/****************************
判斷元件是否為 functional 元件
****************************/
if (isTrue(Ctor.options.functional)) {
return createFunctionalComponent(Ctor, propsData, data, context, children)
}
/****************************
元件上事件的處理,提取元件上的事件監聽器,listeners 是作為子元件的監聽器,不是 真實 DOM 的,然後為了讓父元件在 patch 階段可以處理,所以要替換 .native 修飾符的監聽器
****************************/
const listeners = data.on
data.on = data.nativeOn
/****************************
判斷元件是否為抽象元件
****************************/
if (isTrue(Ctor.options.abstract)) {
const slot = data.slot
data = {}
if (slot) {
data.slot = slot
}
}
installComponentHooks(data)
/****************************
createComponent 最後也是創建 vnode,與 _createElement 創建的 vnode,最大區別在於 componentOptions 上,componentOptions 會保存元件的信息
****************************/
const name = Ctor.options.name || tag
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
if (__WEEX__ && isRecyclableComponent(vnode)) {
return renderRecyclableComponentTemplate(vnode)
}
return vnode
}
舉個例子
Vue.component('current-time', {
data () {
return {
time: new Date()
}
},
template: `<span>{{time}}</span>`
})
var app = new Vue({
el: '#app',
template: `
<div class="hello" @click="addCount">
<span>{{count}}</span>
<current-time></current-time>
</div>
`,
data: {
count: 1
},
methods: {
addCount() {
this.count += 1
}
}
})
我們註冊了自定義的 current-time
元件,在 app 實例中, div 裡有一個 DOM 元素跟自定義元件
_createElement
創建的ㄧ個 vnode,沒有 componentOptions
current-time
元件透過 createComponent
創建的ㄧ個 vnode
,在 componentOptions
上會保存元件的信息
圖片來源:Vue2.x源码解析系列九